home *** CD-ROM | disk | FTP | other *** search
- /*
- FX.C - audio effects for AD1848-based PC soundcards.
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h>
- #include <dos.h>
- #include <conio.h>
- #include "1848.h"
-
- #define BFSZ 4096 /* main delay buffer */
- #define MAX_XTAB 512 /* crossfade lookup table size for pitch change */
- #define NA 0.0 /* param not applicable */
-
- #ifndef TRUE
- #define TRUE 1
- #define FALSE 0
- #endif
-
- /* macros for pitch_change delay index manipulation */
- #define inc_index(x) x = (x + 1) & (BFSZ - 1)
- #define inc_indexes(x1,x2) { x2 = x1; inc_index(x1); }
-
- /* key eye on human every once in a while in all effect routines */
- #define SCAN_HUMAN 0x40000L /* sets rate of checking for key */
- #define check_human() { \
- if(scan-- == 0) { \
- chk_keystroke(); \
- scan = SCAN_HUMAN; \
- } \
- }
-
- /* program description struct */
- struct program {
- char *name; /* ASCII name of program */
- void (*vec)(struct program *p);
- double dry_mix; /* dry (unaffected) signal mix */
- double wet_mix; /* wet (affected) signal mix */
- double feedback; /* feedback */
- double rate; /* usually rate of sweep */
- double depth; /* width of sweep */
- double delay; /* fixed delay factor (base pitch for phaser) */
- } program;
-
- /* handy typedefs */
- typedef union bw {
- unsigned char b[2];
- int w;
- } bw;
- typedef union wl {
- unsigned int w[2];
- long l;
- } wl;
-
- /* protos */
- void thru(struct program *p);
- void noise_gate(struct program *p);
- void flange_chorus(struct program *p);
- void pitch_change(struct program *p);
- void phase_shift(struct program *p);
- void chk_keystroke(void);
- void usage(void);
- void init_1848(int samprate, int mic, int gain);
- void wr_reg(int reg, int val);
- int rd_reg(int reg);
-
- /* table of programs (it's very easy to modify and add programs) */
- struct program programs[] = {
- /* Name/vector Dry Wet Feedback Rate Depth Delay */
- { "Echoes",
- flange_chorus, 0.999, 0.999, 0.7, 0.0, 0.0, 250.0},
- { "Slow flange",
- flange_chorus, 0.999, 0.999, 0.0, 2.0, 6.0, 0.0},
- { "Slow invert flange with feedback",
- flange_chorus, 0.999, -0.999, -0.7, 2.0, 6.0, 0.0},
- { "Slow chorus",
- flange_chorus, 0.999, 0.999, 0.0, 11.0, 20.0, 20.0},
- { "Cheesy \"take me to your leader\" robot voice",
- flange_chorus, 0.0, 0.999, 0.75, 0.0, 0.0, 12.5},
- { "Crazy pitch bend chorus",
- flange_chorus, 0.999, 0.999, 0.3, 150.0, 40.0, 40.0},
- { "Darth",
- pitch_change, 0.0, 0.999, 0.0, -0.35, 25.0, 0.0},
- { "Major third up",
- pitch_change, 0.999, 0.999, 0.0, 0.2599, 35.0, 0.0},
- { "Octave up",
- pitch_change, 0.999, 0.999, 0.0, 1.0, 40.0, 0.0},
- { "Munchkins on helium",
- pitch_change, 0.0, 0.999, 0.4, 0.3348, 35.0, 0.0},
- { "Descending echoes",
- pitch_change, 0.0, 0.999, 0.5, -0.2, 35.0, 120.0},
- { "Ascending echoes",
- pitch_change, 0.0, 0.999, 0.4, 0.2599, 35.0, 120.0},
- { "Phase shift",
- phase_shift, 0.999, 0.999, 0.0, 1.0, 4.0, 100.0},
- { "Slow invert phase shift with feedback",
- phase_shift, 0.999, -0.999, -0.6, 0.2, 6.0, 100.0},
- { "Noise gate",
- noise_gate, NA, NA, NA, 500.0, 0.05, NA},
- { "Straight Thru",
- thru, NA, NA, NA, NA, NA, NA},
- };
- #define NPROGS (sizeof(programs) / sizeof(struct program))
-
- /* globals */
- unsigned int SampleRate; /* sample rate set by init_1848 */
- double Buf[BFSZ]; /* buffer used by delay based effects */
-
-
- /*
- main
- */
- void
- main(int argc, char **argv)
- {
- int patch,samprate,gain;
- struct program *p;
-
- /* make sure we got reasonable # of command line params */
- if(argc < 2 || argc > 4) usage();
-
- /* get pointer to selected program */
- patch = atoi(argv[1]);
- if(patch < 0 || patch > NPROGS)
- usage();
- p = &programs[patch];
-
- /* see if sample rate specified (default to 16k) */
- if(argc > 2)
- samprate = atoi(argv[2]);
- else
- samprate = 16;
-
- /* see if source/gain spec'd */
- if(argc > 3)
- gain = atoi(argv[3]);
- else
- gain = 0x17; /* default: select mic with gain of 15 */
-
- /* init hardware */
- init_1848(samprate,gain & 0x10, gain & 0xf);
-
- /* call the routine spec'd in the program */
- printf("\nPatch %d: %s\n",patch,p->name);
- (*p->vec)(p);
- /* never returns */
- }
-
- /*
- thru
-
- Simply reads 1848 input and routes directly thru to output.
- Useful for setting gain for minimum noise and distortion.
- All program parms ignored.
- */
- void
- thru(struct program *p)
- {
- bw data;
- long scan = 0;
-
- data.w = (int)p->dry_mix; /* quiet the compiler */
-
- /* disable interrupts, go to it */
- disable();
- while(1) {
- while((inp(SR) & 0x20) == 0); /* wait for input ready */
- data.b[0] = inp(PDR); /* read input from chip */
- data.b[1] = inp(PDR);
-
- while((inp(SR) & 0x2) == 0); /* wait for output ready */
- outp(PDR,data.b[0]); /* write output to chip */
- outp(PDR,data.b[1]);
-
- check_human(); /* check on human every so often */
- }
- }
-
-
- /*
- noise_gate
-
- Super simple noise gate to demonstrate how much of the hiss
- comes directly from the ADC on this card, but how quiet the
- DACs are by comparison.
-
- Only parms are:
- rate decay time in ms
- depth threshold for turn on
- */
- void
- noise_gate(struct program *p)
- {
- double inval,decay_fac,gain = 0;
- bw data;
- long scan = 0;
-
- /* calculate decay factor for 20db atten in spec'd time */
- decay_fac = pow(10.0,1.0 / ((p->rate / 1000.0) * (double)SampleRate));
- decay_fac = 1.0 / decay_fac;
-
- /* disable interrupts, go to it */
- disable();
- while(1) {
- while((inp(SR) & 0x20) == 0); /* wait for input ready */
- data.b[0] = inp(PDR); /* read input from chip */
- data.b[1] = inp(PDR);
-
- inval = (double)data.w;
- if(inval > p->depth) /* see if we crossed threshold */
- gain = 1.0; /* turn gate on */
- data.w = (int)(inval * gain);
-
- while((inp(SR) & 0x2) == 0); /* wait for output ready */
- outp(PDR,data.b[0]); /* write output to chip */
- outp(PDR,data.b[1]);
-
- gain *= decay_fac; /* adjust attenuation */
-
- check_human(); /* check on human every so often */
- }
- }
-
-
- /*
- flange_chorus
-
- Does flanging/chorusing family of effects based on a single
- varying delay.
-
- dry_mix mix of unaffected signal (-0.999 to 0.999)
- wet_mix mix of affected signal (-0.999 - 0.999)
- feedback amount of recirculation (-0.9 - 0.9)
- rate rate of delay change in millisecs per sec
- sweep sweep range in millisecs
- delay fixed additional delay in millisecs
- */
- void
- flange_chorus(struct program *p)
- {
- int fp,ep1,ep2;
- int step,depth,delay,min_sweep,max_sweep;
- double inval,outval,ifac = 65536.0;
- long scan = 0;
- bw data;
- wl sweep;
-
- /* fetch params */
- step = (int)(p->rate * 65.536);
- depth = (int)(p->depth * (double)SampleRate / 1000.0);
- delay = (int)(p->delay * (double)SampleRate / 1000.0);
-
- /* init/calc some stuff */
- max_sweep = BFSZ - 2 - delay;
- min_sweep = max_sweep - depth;
- if(min_sweep < 0) {
- printf("Can't do that much delay or depth at this sample rate.\n");
- exit(1);
- }
- sweep.w[1] = (min_sweep + max_sweep) / 2;
- sweep.w[0] = 0;
-
- /* init store and read ptrs to known value */
- fp = ep1 = ep2 = 0;
-
- /* disable interrupts, go to it */
- disable();
- while(1) {
- while((inp(SR) & 0x20) == 0); /* wait for input ready */
- data.b[0] = inp(PDR); /* read input from chip */
- data.b[1] = inp(PDR);
-
- /* interpolate from the 2 read values */
- outval =
- (Buf[ep1] * sweep.w[0] + Buf[ep2] * (ifac - sweep.w[0])) / ifac;
-
- /* store finished input plus feedback */
- Buf[fp] = (inval = (double)data.w) + outval * p->feedback;
-
- /* develop final output mix */
- outval = outval * p->wet_mix + inval * p->dry_mix;
- if(outval > 32767.0)
- data.w = 32767;
- else if(outval < -32768.0)
- data.w = -32768;
- else
- data.w = (int)outval;
-
- while((inp(SR) & 0x2) == 0); /* wait for output ready */
- outp(PDR,data.b[0]); /* write output to chip */
- outp(PDR,data.b[1]);
-
- /* update ptrs */
- fp = (fp + 1) & (BFSZ - 1);
- sweep.l += step;
- ep1 = (fp + sweep.w[1]) & (BFSZ - 1);
- ep2 = (ep1 - 1) & (BFSZ - 1);
-
- /* check for sweep reversal */
- if(sweep.w[1] > max_sweep) /* see if we hit top of sweep */
- step = -step; /* reverse */
- else if(sweep.w[1] < min_sweep) /* or if we hit bottom of sweep */
- step = -step; /* reverse */
-
- check_human(); /* check on human every so often */
- }
- }
-
- /*
- pitch_change
-
- dry_mix mix of unaffected signal (-0.999 to 0.999)
- wet_mix mix of affected signal (-0.999 - 0.999)
- feedback amount of recirculation (-0.9 - 0.9)
- rate amount of pitch change (see table below for values)
- depth sweep range in millisecs for generating pitch shift
- delay fixed additional delay
-
- Semitones Up Down
- 1 0.059463 -0.056126
- 2 0.122462 -0.109101
- 3 0.189207 -0.159104
- 4 0.259921 -0.206299
- 5 0.334840 -0.250846
- 6 0.414214 -0.292893
- 7 0.498307 -0.332580
- 8 0.587401 -0.370039
- 9 0.681793 -0.405396
- 10 0.781797 -0.438769
- 11 0.887749 -0.470268
- 12 1.000000 -0.500000
- */
- void
- pitch_change(register struct program *p)
- {
- int fp,ep1,ep2,ep3,ep4;
- int depth,delay,min_sweep,max_sweep,sweep_up;
- int i,step,xfade,xfade_cnt,active,active_cnt,chanA;
- long scan = 0;
- double inval,outval,comp,ifac = 65536.0;
- double blendA,blendB,*fadeA,*fadeB;
- static double fade_out[MAX_XTAB],fade_in[MAX_XTAB];
- bw data;
- wl sweep;
-
- /* fetch params */
- step = (int)(p->rate * 65535.0);
- sweep_up = p->rate > 0;
- depth = (int)(p->depth * (double)SampleRate / 1000.0);
- delay = (int)(p->delay * (double)SampleRate / 1000.0);
- xfade = (int)(12.0 * (double)SampleRate / 1000.0);
-
- /* init/calc some stuff */
- max_sweep = BFSZ - 2 - delay;
- min_sweep = max_sweep - depth;
- active = max_sweep - min_sweep - (int)(xfade * p->rate) - 2;
- if(xfade > MAX_XTAB) {
- printf("Can't do pitch change crossfade at this sample rate.\n");
- exit(1);
- }
- if(min_sweep < 0) {
- printf("Can't do that much delay or depth at this sample rate.\n");
- exit(1);
- }
-
- /* build the crossfade lookup tables */
- for(i = 0; i < xfade; i++) {
- fade_in[i] = cos((double)i * M_PI_2 / (double)xfade);
- fade_out[i] = sin((double)i * M_PI_2 / (double)xfade);
- }
-
- /* init store and read ptrs to known value, chanA active 1st */
- fp = ep3 = ep4 = xfade_cnt = 0;
- sweep.l = 0;
- if(sweep_up)
- ep1 = ep2 = min_sweep;
- else
- ep1 = ep2 = max_sweep;
- active_cnt = active;
- blendA = 1.0;
- blendB = 0.0;
- fadeA = fade_out;
- fadeB = fade_in;
- chanA = TRUE;
-
- /* disable interrupts, go to it */
- disable();
- while(1) {
- while((inp(SR) & 0x20) == 0); /* wait for input ready */
- data.b[0] = inp(PDR); /* read input from chip */
- data.b[1] = inp(PDR);
-
- /* messy expression to interpolate from both pairs of read ptrs */
- comp = ifac - sweep.w[0];
- outval =
- ((Buf[ep1] * sweep.w[0] + Buf[ep2] * comp) * blendA +
- (Buf[ep3] * sweep.w[0] + Buf[ep4] * comp) * blendB) / ifac;
-
- /* store finished input plus feedback */
- Buf[fp] = (inval = (double)data.w) + outval * p->feedback;
-
- /* develop final output mix */
- outval = outval * p->wet_mix + inval * p->dry_mix;
- if(outval > 32767.0) /* clip output if necessary */
- data.w = 32767;
- else if(outval < -32768.0)
- data.w = -32768;
- else
- data.w = (int)outval;
-
- while((inp(SR) & 0x2) == 0); /* wait for output ready */
- outp(PDR,data.b[0]); /* write output to chip */
- outp(PDR,data.b[1]);
-
- check_human(); /* check on human every so often */
-
- /* see if crossfade active */
- if(xfade_cnt) {
- xfade_cnt--;
- blendA = fadeA[xfade_cnt];
- blendB = fadeB[xfade_cnt];
- }
-
- /* update store ptr */
- inc_index(fp);
-
- /* see which direction */
- if(sweep_up) {
- /* update sweep */
- sweep.l += (unsigned)step;
-
- /* always inc at least once */
- inc_indexes(ep1,ep2);
- inc_indexes(ep3,ep4);
-
- /* if sweep didn't overflow, we're done */
- if(sweep.w[1] == 0) continue;
-
- /* sweep overflowed, inc again */
- inc_indexes(ep1,ep2);
- inc_indexes(ep3,ep4);
- sweep.w[1] = 0;
-
- /* see if it's time to switch over to other delay channel */
- if(active_cnt-- == 0) {
- xfade_cnt = xfade; /* initiate crossfade */
- active_cnt = active; /* start counter on new channel */
- if(chanA) { /* A has been active, go to B */
- chanA = FALSE;
- ep3 = (fp + min_sweep) & (BFSZ - 1);
- fadeA = fade_out;
- fadeB = fade_in;
- }
- else {
- chanA = TRUE;
- ep1 = (fp + min_sweep) & (BFSZ - 1);
- fadeB = fade_out;
- fadeA = fade_in;
- }
- }
- }
- /* do downward sweep */
- else {
- sweep.l += step; /* update sweep */
-
- /* if sweep didn't overflow, inc ptrs, that's all */
- if(sweep.w[1] == 0) {
- inc_indexes(ep1,ep2);
- inc_indexes(ep3,ep4);
- continue;
- }
- /* sweep overflowed, check on stuff but skip ptr inc */
- sweep.w[1] = 0;
-
- /* see if it's time to switch over to other delay channel */
- if(active_cnt-- == 0) {
- xfade_cnt = xfade;
- active_cnt = active;
- if(chanA) { /* A has been active, go to B */
- chanA = FALSE;
- ep3 = (fp + max_sweep) & (BFSZ - 1);
- fadeA = fade_out;
- fadeB = fade_in;
- }
- else {
- chanA = TRUE;
- ep1 = (fp + max_sweep) & (BFSZ - 1);
- fadeB = fade_out;
- fadeA = fade_in;
- }
- }
- } /* end down sweep */
- } /* end main loop */
- }
-
- /*
- phase_shift
-
- Digital version of the popular '70s effect. This one
- does 4 stages just like old MXR Phase 90 stompbox.
-
- dry_mix mix of unaffected signal (-0.999 to 0.999)
- wet_mix mix of affected signal (-0.999 - 0.999)
- feedback amount of recirculation (-0.9 - 0.9)
- rate rate of sweep in cycles per second
- depth sweep range in octaves
- delay base frequency of sweep
- */
- void
- phase_shift(register struct program *p)
- {
- long scan = 0;
- double wp,min_wp,max_wp,range,coef,sweepfac;
- double inval,x1,outval = 0.0;
- static double lx1,ly1,lx2,ly2,lx3,ly3,lx4,ly4;
- bw data;
-
- /* calc params for sweeping filters */
- wp = min_wp = (M_PI * p->delay) / (double)SampleRate;
- range = pow(2.0,p->depth);
- max_wp = (M_PI * p->delay * range) / (double)SampleRate;
- p->rate = pow(range,p->rate / ((double)SampleRate / 2));
- sweepfac = p->rate;
-
- /* disable interrupts, go to it */
- disable();
- while(1) {
- coef = (1.0 - wp) / (1.0 + wp); /* calc coef for current freq */
-
- while((inp(SR) & 0x20) == 0); /* wait for input ready */
- data.b[0] = inp(PDR); /* read input from chip */
- data.b[1] = inp(PDR);
-
- x1 = (inval = (double)data.w) + p->feedback * ly4;
- ly1 = coef * (ly1 + x1) - lx1; /* do 1st filter */
- lx1 = x1;
- ly2 = coef * (ly2 + ly1) - lx2; /* do 2nd filter */
- lx2 = ly1;
- ly3 = coef * (ly3 + ly2) - lx3; /* do 3rd filter */
- lx3 = ly2;
- ly4 = coef * (ly4 + ly3) - lx4; /* do 4th filter */
- lx4 = ly3;
-
- /* develop final output mix */
- outval = ly4 * p->wet_mix + inval * p->dry_mix;
- if(outval > 32767.0) /* clip output if necessary */
- data.w = 32767;
- else if(outval < -32768.0)
- data.w = -32768;
- else
- data.w = (int)outval;
-
- while((inp(SR) & 0x2) == 0); /* wait for output ready */
- outp(PDR,data.b[0]); /* write output to chip */
- outp(PDR,data.b[1]);
-
- wp *= sweepfac; /* adjust freq of filters */
- if(wp > max_wp) /* max? */
- sweepfac = 1.0 / p->rate; /* sweep back down */
- else if(wp < min_wp) /* min? */
- sweepfac = p->rate; /* sweep back up */
-
- check_human(); /* check on human every so often */
- }
- }
-
-
- /*
- chk_keystroke
-
- Sees if human has hit a key. If so, exits. This routine makes
- a click cuz it has to turn on interrupts briefly.
- */
- void
- chk_keystroke(void)
- {
- enable();
- if(kbhit()) {
- getch();
- exit(0);
- }
- disable();
- }
-
- /*
- usage
- */
- void
- usage(void)
- {
- int i;
- static char *use =
- "Usage: fx [patch] [[samp_rate]] [[src_gain]]\n"
- " patch Selects effect program. Value should be 0-%d.\n"
- " samp_rate Selects sampling frequency. Value should be 5,6,8,\n"
- " 9,11,16,18,22,27,32,33,37,44, or 48. Defaults to 16k.\n"
- " src_gain Selects source and gain. 0-15 adjust line input level.\n"
- " 16-31 select the microphone input and adjust its level.\n"
- " Defaults to 23 for microphone input.\n";
-
- printf(use,NPROGS - 1);
- for(i = 0; i < NPROGS; i++)
- printf("Patch %2d:\t%s\n",i,programs[i].name);
- exit(1);
- }
-
-
- int iar_mce;
-
- /*
- init_1848
-
- Sets up for programmed I/O at spec'd sample rate. Initiates
- simultaneous capture and play mode.
- */
- void
- init_1848(int samprate,int mic,int gain)
- {
- int i,version;
- struct sr {
- int handle; /* handy value for humans */
- unsigned int actual; /* actual sample rate */
- int code; /* code for chip */
- };
- struct sr *srp;
- struct sr sr_tab[NUM_SRATES] = {
- {5,5152,S5152}, {6,6615,S6615}, {8,8000,S8000},
- {9,9600,S9600}, {11,11025,S11025}, {16,16000,S16000},
- {18,18900,S18900}, {22,22050,S22050}, {27,27428,S27428},
- {32,32000L,S32000}, {33,33075L,S33075}, {37,37800L,S37800},
- {44,44100L,S44100}, {48,48000L,S48000}};
-
- /* look up code for spec'd sample rate */
- for(i = 0, srp = sr_tab; i < NUM_SRATES; i++, srp++) {
- if(srp->handle == samprate || srp->actual == samprate)
- break;
- }
- if(i == NUM_SRATES) usage();
-
- delay(0); /* calibrate delay routine */
-
- version = rd_reg(MISCINFO); /* see what kind of chip we have */
- if(version == 0xff) {
- printf("Can't find 1848 chip.\n");
- exit(1);
- }
- printf("Found AD1848%c CODEC\n",(version & 0xf) + 'A');
- /* NOTE: the rev K chip is reportedly much quieter and worth getting */
-
- /* prepare to change/init chip setup */
- wr_reg(LOUTCTL,0x80); /* mute left DAC */
- wr_reg(ROUTCTL,0x80); /* mute right DAC */
- wr_reg(DIGMIX,0); /* mute digital mix */
- wr_reg(LAUX1CTL,0x80); /* mute left aux1 in */
- wr_reg(RAUX1CTL,0x80); /* mute right aux1 in */
- wr_reg(LAUX2CTL,0x80); /* mute left aux2 in */
- wr_reg(RAUX2CTL,0x80); /* mute right aux2 in */
-
- /* change chip config */
- outp(IAR,iar_mce = 0x40); /* allow mode changes */
- wr_reg(INTCFG,0xc8); /* set auto calibrate */
- wr_reg(CLKDATFMT,0x40 | srp->code);
-
- while(inp(IAR) & 0x80) /* wait for chip to come out of init */
- ;
-
- outp(IAR,iar_mce = 0x00); /* clear mode change enable */
- delay(2000); /* wait for auto calib to kick in */
-
- while(rd_reg(TSTINIT) & 0x20) /* wait for auto calibration */
- ;
-
- wr_reg(LOUTCTL,0); /* unmute the DACs */
- wr_reg(ROUTCTL,0);
- i = (mic ? 0xa0 : 0x40) | (gain & 0xf);/* decide gain and signal source */
- wr_reg(LINCTL,i); /* unmute the inputs */
- wr_reg(RINCTL,i);
- wr_reg(INTCFG,0xc3); /* kick off capture and play */
-
- printf("Sample rate initialized to %u.\n",srp->actual);
- SampleRate = srp->actual;
- }
-
- /*
- wr_reg
- */
- void
- wr_reg(int reg, int val)
- {
- outp(IAR,iar_mce | reg);
- outp(IDR,val);
- }
-
- /*
- rd_reg
- */
- int
- rd_reg(int reg)
- {
- outp(IAR,iar_mce | reg);
- return(inp(IDR));
- }